package com.easyaop.api;

import com.easyaop.api.advisor.*;
import com.easyaop.api.around.Around;
import com.easyaop.api.order.OrderList;
import com.easyaop.api.event.listener.MethodListener;
import com.easyaop.api.pointcut.ClassObject;
import com.easyaop.api.pointcut.Pointcut;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

public final class Context {
//    private static final List<Around> EMPTY_AROUND_SAFE_LIST = new OrderList<>();

    public final static Set<Advisor> ADVISORS = Collections.synchronizedSet(new LinkedHashSet<>());

    /**
     * 方法签名 格式 包名.类名.方法名desc
     * com.MyBean.add(II)I
     */
    private final static ConcurrentMap<String, OrderList<MethodListener>> _SIGNATURES = new ConcurrentHashMap<>();

    private final static ConcurrentMap<String, OrderList<Around>> AROUND_SIGNATURES = new ConcurrentHashMap<>();

    public static synchronized void removeListener(Set<String> signatures, Set<MethodListener> listener) {
        for (String signature : signatures) {
            OrderList<MethodListener> orderList = _SIGNATURES.get(signature);
            if (orderList == null) {
                continue;
            }
            orderList.removeIf(methodListenerHandlerOld -> {
                for (MethodListener methodListenerHandler : listener) {
                    if (methodListenerHandlerOld == methodListenerHandler) {
                        return true;
                    }
                }
                return false;
            });
        }
    }

    public synchronized static EventAdvisor addListener(Pointcut pointcut, MethodListener listener) {

        Optional<Advisor> first = Context.ADVISORS.stream()
                .filter(EventAdvisor.class::isInstance)
                .filter(pointcut::equals)
                //                .map(EventAdvisor::ca)
                .findFirst();

        if (first.isPresent()) {
            Advisor advisor = first.get();
            ((EventAdvisor) advisor).addListener(listener);
            return null;
        } else {
            EventAdvisor advisor = new EventAdvisor(pointcut);
            advisor.addListener(listener);
            Context.ADVISORS.add(advisor);
            return advisor;
        }
    }

    public synchronized static void addListener(Set<String> signatures, Set<MethodListener> listeners) {
        for (String signature : signatures) {
            OrderList<MethodListener> methodListeners = _SIGNATURES.computeIfAbsent(signature, k -> new OrderList<>());
            for (MethodListener listener : listeners) {
                methodListeners.create(listener);
            }
        }
    }

    public static Iterable<MethodListener> getListeners(String signature) {
        OrderList<MethodListener> orderList = _SIGNATURES.get(signature);
        if (orderList == null || !orderList.iterator().hasNext()) {
            return Collections.EMPTY_LIST;
        }
        return orderList;
    }

    public synchronized static void registerAround(String signature, Iterable<Around> aroundItr) {
        registerAround(Collections.singleton(signature), aroundItr);
    }

    public synchronized static void registerAround(Set<String> signatures, Iterable<Around> aroundItr) {
        for (String signature : signatures) {
            OrderList<Around> orderList = AROUND_SIGNATURES.computeIfAbsent(signature, k -> new OrderList<>());
            for (Around around : aroundItr) {
                orderList.create(around);
            }
        }
    }

    public synchronized static Iterable<Around> getArounds(String signature) {
        OrderList<Around> orderList = AROUND_SIGNATURES.get(signature);
        if (orderList == null || !orderList.iterator().hasNext()) {
            return Collections.EMPTY_LIST;
        }
        return orderList;
    }

    public static synchronized void removeAround(AroundAdvisor advisor, Iterable<Around> aroundItr) {
        for (String signature : advisor.getSignatures()) {
            OrderList<Around> orderList = AROUND_SIGNATURES.get(signature);
            if (orderList == null) {
                continue;
            }
            orderList.removeIf(aroundOld -> {
                for (Around around : aroundItr) {
                    if (aroundOld == around) {
                        return true;
                    }
                }
                return false;
            });
        }
    }

    public static synchronized void removeAdvisor(Advisor advisor) {
        ADVISORS.remove(advisor);
    }

    public static <T extends Advisor> Optional<T> getAdvisor(Pointcut pointcut, Class<T> advisorClass) {
        return Context.ADVISORS.stream()
                .filter(value -> value.getPointcut().equals(pointcut) && value.getClass().equals(advisorClass))
                .findFirst()
                .map(advisorClass::cast);
    }

    /**
     * 快速判断，有误差
     * 如果一定不匹配返回false
     * 否则 返回true
     */
    public static boolean hasPointcut(String className) {
        Advisor[] advisors = Context.ADVISORS.toArray(new Advisor[0]);
        for (Advisor advisor : advisors) {
            if (advisor.getPointcut().getClass().getName().contains("AspectjExpressionPointcut")) {
                return true;// 匹配成本高
            }
            Pointcut pointcut = advisor.getPointcut();
            if (pointcut.isClass(new ClassObject.ForUnloaded(className))) {
                return true;
            }
        }
        return false;
    }

    public static boolean isTransformed(String className) {
        return Arrays.stream(ADVISORS.toArray(new Advisor[0]))
                .map(Advisor::getSignatures)
                .flatMap(Set::stream)
                .distinct()
                .map(Context::getClassName)
                .anyMatch(className::equals);
    }

    private static String getClassName(String signatures) {
        return signatures.split("#")[0];

    }
}
